#include "http.h"
using namespace std;
#define PROT_1_0 MAKEWORD(0,1)
#define PROT_1_1 MAKEWORD(1,1)
#define SERVER "CPP-HTTPD/1.0/Gerald"
#define GMT "%a, %d %b %Y %H:%M:%S GMT"

bool caseCompare(string s1,string s2) {
	if(s1.size()!=s2.size()) return false;
	for(int i=0;i<s1.size();i++) s1[i]=tolower(s1[i]);
	for(int i=0;i<s2.size();i++) s2[i]=tolower(s2[i]);
	return s1.compare(s2)==0;
}

bool Headers::GetHeader(string key,string *val) {
	Iterator hi;
	hi=headers.find(Capitalize(key));
	if(hi!=headers.end()) {
		*val=hi->second;
		return true;
	}
	return false;
}
void Headers::SetHeader(string key,string val) {
	headers[Capitalize(key)]=val;
}
void Headers::DelHeader(string key) {
	headers.erase(Capitalize(key));
}
void Headers::Clear() {
	headers.clear();
}
string Headers::Capitalize(string s) {
	for(int i=0;i<s.size();i++)
		if(!i||!isalpha(s[i-1])) s[i]=toupper(s[i]);
		else s[i]=tolower(s[i]);
	return s;
}
Headers::Iterator Headers::Begin() {
	return headers.begin();
}
Headers::Iterator Headers::End() {
	return headers.end();
}

Request::Request(THREAD_DATA *td) {
	this->td=td;
}
void Request::Init() {
	host="";
	protocol=0;
	headers.Clear();
}

Response::Response(THREAD_DATA *td) {
	this->td=td;
}
void Response::Init() {
	alive=false;
	headers_sent=false;
	//chunked=false;	// will be reset in SendHeaders
	protocol=PROT_1_0;
	SetStatus(200);
	headers.Clear();
	error=false;
	ok=true;
	bytes_sent=0;
}
void Response::SetStatus(int code,string message) {
	this->status=code;
	if(message.empty()) switch(code) {
	case 200:
		message="OK";
		break;
	case 204:
		message="No Content";
		break;
	case 206:
		message="Partial Content";
		break;
	case 301:
		message="Moved Permanently";
		break;
	case 302:
		message="Found";
		break;
	case 303:
		message="See Other";
		break;
	case 304:
		message="Not Modified";
		break;
	case 307:
		message="Temporary Redirect";
		break;
	case 400:
		message="Bad Request";
		break;
	case 403:
		message="Forbidden";
		break;
	case 404:
		message="Not Found";
		break;
	case 500:
		message="Internal Server Error";
		break;
	case 501:
		message="Not Implemented";
		break;
	default:
		message="Unknown";
	}
	this->message=message;
}
void Response::SetStatus(int code) {
	SetStatus(code,"");
}
void Response::Flush(const char *buf,int size) {
	stringstream t;
	string st;
	if(headers_sent&&chunked) {
		t<<hex<<size<<endl;
		st=t.str();
		ok=ok&&send(td->sock,(char *)st.c_str(),st.size(),0)>0;
		if(ok) bytes_sent+=st.size();
	}
	if(size) {
		ok=ok&&send(td->sock,(char *)buf,size,0)>0;
		if(ok) bytes_sent+=size;
	}
	if(headers_sent&&chunked) {
		st="\r\n";
		ok=ok&&send(td->sock,(char *)st.c_str(),st.size(),0)>0;
		if(ok) bytes_sent+=st.size();
	}
}
void Response::Flush() {
	if(ss.tellp()) {
		string st=ss.str();
		Flush(st.c_str(),st.size());
		ss.str("");
		ss.clear();
	} else Flush(NULL,0);
}
void Response::SendHeaders() {
	ss<<"HTTP/"<<(protocol>>8)<<"."<<(protocol&255)<<" "<<status<<" "<<message<<endl;
	chunked=true;
	string st;
	if(status==204||status==304
		||headers.GetHeader("Content-Length",&st)
		||protocol<PROT_1_1) {
		chunked=false;
		headers.DelHeader("Transfer-Encoding");
	} else if(headers.GetHeader("Transfer-Encoding",&st))
		chunked=caseCompare(st,"chunked");
	else if(chunked)
		headers.SetHeader("Transfer-Encoding","chunked");
	else	// no Content-Length and not chunked
		alive=false;
	headers.SetHeader("Connection",alive?"keep-alive":"close");
	for(Headers::Iterator hi=headers.Begin();hi!=headers.End();hi++) {
		ss<<hi->first<<": "<<hi->second<<endl;
	}
	ss<<endl;
	//cout<<ss.str()<<endl;
	Flush();
	headers_sent=true;
}
void Response::Write(const char *data,int size) {
	if(!headers_sent) SendHeaders();
	if(!size) return;
	if(ss.tellp()) Flush();
	Flush(data,size);
}
void Response::Write(string data) {
	if(!headers_sent) SendHeaders();
	if(data.empty()) return;
	if(int i=ss.tellp())
		if(i+data.size()>BUFSIZE) Flush();
	ss<<data;
}
void Response::End() {
	if(!headers_sent) {
		if(status!=204&&status!=304) headers.SetHeader("Content-Length","0");
		SendHeaders();
	}
	if(ss.tellp()) Flush();
	Flush();
}

HTTPHandler::HTTPHandler(THREAD_DATA *td) {
	//cout<<"=== Start handler ==="<<endl;
	this->td=td;
	req=new Request(td);
	res=new Response(td);
	int t=3000;
	setsockopt(td->sock,SOL_SOCKET,SO_SNDTIMEO,(char *)&t,sizeof(t));
	setsockopt(td->sock,SOL_SOCKET,SO_RCVTIMEO,(char *)&t,sizeof(t));
}
HTTPHandler::~HTTPHandler() {
	closesocket(td->sock);
	delete td->addr;
	delete td;
	delete res;
	delete req;
	//cout<<"=== End handler ==="<<endl;
}
int HTTPHandler::ReadData(stringstream *ss) {
	ss->str("");
	ss->clear();
	string st;
	char buf[BUFSIZE];
	int r;
	r=recv(td->sock,buf,BUFSIZE,0);
	if(r>0) {
		st.assign(buf,r);
		*ss<<buf;
	}
	return r;
}
int HTTPHandler::ReadLine(stringstream *ss,string *st) {
	string tmp;
	st->assign("");
	while(true) {
		getline(*ss,tmp);
		if(ss->eof()) {
			st->append(tmp);
			if(st->size()>BUFSIZE) return -2;	// LINE TOO LONG
			int r=ReadData(ss);
			if(r<=0) return -3;	// Connection closed
		} else break;
	}
	st->append(tmp);
	return st->size();
}
int HTTPHandler::ParseRequest() {
	int r,i,l;
	string st;
	stringstream ss,sl;
	char buf[6];
	r=ReadLine(&ss,&req->request_line);
	res->ts=clock();
	if(r<=0) return r;
	timestamp=time(NULL);
	clock_start=clock();
	//cout<<req->request_line<<endl;
	if(req->request_line.back()=='\r') req->request_line.pop_back();
	sl.str(req->request_line);
	sl>>req->method>>req->request_uri;
	if(sl.fail()) return -1;
	sl.getline(buf,6,'/');
	if(sl.fail()||strcmp(buf," HTTP")) return -1;
	sl>>r>>buf[0]>>i;
	if(sl.fail()) return -1;
	req->protocol=MAKEWORD(i,r);
	if(req->protocol>=PROT_1_1) {
		res->protocol=PROT_1_1;
		res->alive=true;	// >= HTTP/1.1: default Keep-alive
	} else res->protocol=PROT_1_0;
	while(true) {
		r=ReadLine(&ss,&st);
		if(r<0) return r;
		// !r => l=-1
		l=st.find_last_not_of(" \t\r\n");
		if(l<0) break;
		//cout<<st<<endl;
		r=st.find(':');
		i=st.find_first_not_of(' ',r+1);
		req->headers.SetHeader(st.substr(0,r),st.substr(i,l-i+1));
	}
	//cout<<"--------------------"<<endl;
	if(req->headers.GetHeader("Connection",&st)) res->alive=caseCompare(st,"keep-alive");
	ServerItem *sconf=((HTTPServer *)td->server)->conf;
	if(req->headers.GetHeader("Host",&st)) {
		req->host=st;
		for(vector<HostItem *>::iterator hi=sconf->hosts.begin();hi!=sconf->hosts.end();hi++)
			if(!(*hi)->name.compare(st)) {
				hconf=*hi;
				break;
			}
	}
	if(!hconf&&sconf->hosts.size()) hconf=sconf->hosts[0];
	return 1;
}
int HTTPHandler::Hex2Dec(string s) {
	int x=0;
	for(int i=0;i<s.size();i++) {
		x<<=4;
		char c=s.at(i);
		if(c>='0'&&c<='9') x+=c-'0';
		else if(c>='A'&&c<='F') x+=c-'A'+10;
		else if(c>='a'&&c<='f') x+=c-'a'+10;
		else return -1;
	}
	return x;
}
string HTTPHandler::Unquote(string s) {
	string t="";
	for(int i=0;i<s.size();i++) {
		if(s[i]=='%') {
			if(i+1<s.size()&&s[i+1]=='%') {
				t.append(1,'%');
				i++;
				continue;
			} else if(i+2<s.size()) {
				int d=Hex2Dec(s.substr(i+1,2));
				if(d<0) t.append(s.substr(i,3));
				else t.append(1,(char)d);
				i+=2;
				continue;
			}
		}
		t.append(1,s[i]);
	}
	return t;
}
string HTTPHandler::UTF8TOACP(string s) {
	int l;
	const char *p=s.c_str();
	l=MultiByteToWideChar(CP_UTF8,0,p,-1,NULL,0);
	LPWSTR q=new WCHAR[l*2+2];
	MultiByteToWideChar(CP_UTF8,0,p,-1,q,l);
	l=WideCharToMultiByte(CP_ACP,0,q,-1,NULL,0,NULL,NULL);
	char *b=new char[l+1];
	WideCharToMultiByte(CP_ACP,0,q,-1,b,l,NULL,NULL);
	delete q;
	s=string(b);
	delete b;
	return s;
}
void HTTPHandler::Redirect(string path,int code) {
	res->SetStatus(code);
	res->headers.SetHeader("Content-type","text/html");
	res->headers.SetHeader("Location",path);
	res->Write("This page has been moved <a href='");
	res->Write(path);
	res->Write("'>here</a>");
	switch(code) {
	case 301:
		res->Write("permanently");
		break;
	case 303:
		res->Write("temporarily");
		break;
	}
	res->Write(".");
}
void HTTPHandler::Redirect(string path) {
	Redirect(path,303);
}
void HTTPHandler::WriteHTMLHeader(string title) {
	res->Write("\
<!DOCTYPE html><html><head>\
<meta charset=gbk>\
<meta name=viewport content='width=device-width'>\
<title>");
	res->Write(title);
	res->Write("</title></head><style>\
body{font-family:Tahoma;background:#eee;color:#333;}\
a{text-decoration:none;}\
a:hover{text-decoration:underline;}\
ul{margin:0;padding-left:20px;}\
li a{display:block;word-break:break-all;}\
</style><body>");
}
void HTTPHandler::WriteHTMLFooter(string footer) {
	res->Write("<hr>");
	res->Write(footer);
	res->Write("<center>&copy; Designed by <a href=http://geraldl.net>Gerald</a> 2014</center></body></html>");
}
void HTTPHandler::SendError(int code,string message) {
	if(!res->error) {
		res->error=true;
		res->SetStatus(code,message);
	}
	res->headers.SetHeader("Content-Type","text/html");
	stringstream ss;
	ss<<"Error "<<res->status<<": "<<res->message;
	WriteHTMLHeader(ss.str());
	ss.str("");
	ss<<"<h1>Error response</h1><p>Error code: "<<res->status<<"</p><p>Message: "<<res->message<<"</p>";
	res->Write(ss.str());
	WriteHTMLFooter("");
}
void HTTPHandler::SendError(int code) {
	SendError(code,"");
}
bool HTTPHandler::IsOuterURL(string path) {
	for(int i=0;i<path.size();i++)
		if(!isalpha(path[i])) {
			return path.compare(i,3,"://")==0;
		}
	return false;
}
int HTTPHandler::ParseURL(string path) {
	if(hconf) for(vector<RewriteItem>::iterator ri=hconf->rr.begin();ri!=hconf->rr.end();ri++) {
		regex reg=ri->reg;
		smatch result;
		if(regex_search(path,result,reg)) {
			path=result.format(ri->dest.c_str());
			break;
		}
	}
	req->rewrited_path=path;
	if(IsOuterURL(path)) return 0;
	if(path[0]!='/') return -1;
	int i=path.find('?');
	if(i>0) {
		req->query_string.assign(path,i+1,-1);
		path.erase(i,-1);
	} else {
		req->query_string="";
	}
	path=NormalPath(path);
	path=Unquote(path);
	path=UTF8TOACP(path);
	req->path=path;
	/*cout<<"RequestURI: "<<req->rewrited_path<<endl
		<<"Path: "<<req->path<<endl
		<<"QueryString: "<<req->query_string<<endl
		<<"-----------------"<<endl;*/
	return IsOuterURL(req->rewrited_path)?0:1;
}
int HTTPHandler::GetRealPath() {
	AliasItem *a=NULL;
	int l;
	if(hconf) for(vector<AliasItem>::iterator ai=hconf->alias.begin();ai!=hconf->alias.end();ai++) {
		string s=ai->src;
		l=s.size();
		if((!l||!req->path.compare(0,l,s))&&(!req->path[l]||req->path[l]=='/')) {
			a=&(*ai);
			break;
		}
	}
	if(a) {
		req->root=a->dest;
		req->file_path=a->dest+req->path.substr(a->src.size(),-1);
	} else return -1;
	bool dir=req->file_path.back()=='/';
	if(dir) req->file_path.pop_back();
	l=stat(req->file_path.c_str(),&st);	// 0 for success
	if(dir) {
		req->file_path.push_back('/');
		if(!S_ISDIR(st.st_mode)) l=-1;
	}
	return l;
}
void HTTPHandler::ListDir() {
	struct _finddata_t data;
	string s=req->file_path+"*.*";
	int handle=_findfirst(s.c_str(),&data);
	if(handle==-1) {
		SendError(500,"Error listing directory");
		return;
	}
	WriteHTMLHeader("Directory listing for "+req->path);
	res->Write("<h1>Directory listing</h1>");
	stringstream ss;
	for(int i=0,l=req->path.size();;) {
		int j=req->path.find('/',i);
		if(j>=0&&j+1==l) j=-1;
		if(j>=0)
			ss<<"<a href='"<<req->path.substr(0,j+1)<<"'>";
		ss<<(i?req->path.substr(i,j<0?-1:j-i):"Home");
		if(j>=0) {
			ss<<"</a>/";
			i=j+1;
		} else break;
	}
	s=ss.str();
	res->Write(s);
	res->Write("<hr><ul>");
	bool none=true;
	while(true) {
		if(data.name[0]!='.'&&strcmp(data.name,"desktop.ini")) {
			bool dir=data.attrib&_A_SUBDIR;
			res->Write("<li><a href='");
			res->Write(data.name);
			if(dir) res->Write("/");
			res->Write("'>");
			if(dir) res->Write("<b>&lt;");
			res->Write(data.name);
			if(dir) res->Write("&gt;</b>");
			res->Write("</a></li>");
			none=false;
		}
		if(_findnext(handle,&data)) break;
	}
	if(none) res->Write("<li>Null</li>");
	_findclose(handle);
	res->Write("</ul>");
	WriteHTMLFooter(s);
}
void HTTPHandler::SendFile() {
	string s;
	stringstream ss;
	long long l,r1,r2=-1;
	res->headers.SetHeader("Accept-Ranges","bytes");
	if(req->headers.GetHeader("Range",&s)) {	// bytes=r1-[r2]
		char c;
		ss.str(s);
		//assert !s.compare(0,6,"bytes=");
		ss.ignore(6,'=');
		ss>>r1>>c;
		if(!ss.fail()) {
			ss>>r2;if(ss.fail()) r2=st.st_size-1;
			if(r1<=r2&&r2<st.st_size) {
				res->SetStatus(206);
				ss.clear();ss.str("bytes ");
				ss<<r1<<"-"<<r2<<"/"<<st.st_size;
				res->headers.SetHeader("Content-Range",ss.str());
				l=r2-r1+1;
			} else r2=-1;
		}
	}
	if(r2<0) {l=st.st_size;r1=0;}
	ss.clear();ss.str("");ss<<l;
	res->headers.SetHeader("Content-Length",ss.str());
	ifstream is(req->file_path,ios::binary);
	char buf[BUFSIZE];
	is.seekg(r1);
	while(l>0&&res->ok) {
		r2=l>BUFSIZE?BUFSIZE:l;
		is.read(buf,r2);
		res->Write(buf,r2);
		l-=r2;
	}
	is.close();
}
MIMEType *HTTPHandler::GuessType() {
	int i=req->file_path.find_last_of("./");
	string s;
	if(i<0||req->file_path[i]!='.') s="";
	else s=req->file_path.substr(i+1,-1);
	MIMEMap *pmime=((HTTPServer *)td->server)->pmime;
	MIMEMap::iterator mi=pmime->find(s);
	if(mi!=pmime->end()) return mi->second;
	return NULL;
}
bool HTTPHandler::CheckModified() {
	MIMEType *m=GuessType();
	if(!m) {
		res->headers.SetHeader("Content-Type","application/octet-stream");
		return true;
	}
	res->headers.SetHeader("Content-Type",m->type);
	if(m->expire) {
		stringstream ss;
		char buf[256];
		struct tm *ft,t;
		string s;
		ss<<"max-age="<<m->expire<<", must-revalidate";
		res->headers.SetHeader("Cache-Control",ss.str());
		strftime(buf,256,GMT,ft=gmtime(&st.st_mtime));
		res->headers.SetHeader("Last-Modified",buf);
		if(req->headers.GetHeader("If-Modified-Since",&s)) {
			ss.str(s);ss.clear();
			ss>>s;
			if(ss.fail()) return true;
			if(!s.compare("Sun")) t.tm_wday=0;
			else if(!s.compare("Mon")) t.tm_wday=1;
			else if(!s.compare("Tue")) t.tm_wday=2;
			else if(!s.compare("Wed")) t.tm_wday=3;
			else if(!s.compare("Thu")) t.tm_wday=4;
			else if(!s.compare("Fri")) t.tm_wday=5;
			else if(!s.compare("Sat")) t.tm_wday=6;
			else return true;
			ss>>t.tm_mday>>s;
			if(ss.fail()) return true;
			if(!s.compare("Jan")) t.tm_mon=0;
			else if(!s.compare("Feb")) t.tm_mon=1;
			else if(!s.compare("Mar")) t.tm_mon=2;
			else if(!s.compare("Apr")) t.tm_mon=3;
			else if(!s.compare("May")) t.tm_mon=4;
			else if(!s.compare("Jun")) t.tm_mon=5;
			else if(!s.compare("Jul")) t.tm_mon=6;
			else if(!s.compare("Aug")) t.tm_mon=7;
			else if(!s.compare("Sep")) t.tm_mon=8;
			else if(!s.compare("Oct")) t.tm_mon=9;
			else if(!s.compare("Nov")) t.tm_mon=10;
			else if(!s.compare("Dec")) t.tm_mon=11;
			else return true;
			ss>>t.tm_year>>t.tm_hour>>t.tm_min>>t.tm_sec>>s;
			if(ss.fail()||s.compare("GMT")) return true;
			t.tm_isdst=0;
			//cout<<mktime(&t)<<mktime(ft)<<st.st_mtime<<endl;
			if(mktime(&t)>=mktime(ft)) {
				res->SetStatus(304);
				return false;
			}
		}
	}
	return true;
}
void HTTPHandler::DoGET() {
	if(S_ISDIR(st.st_mode)) {
		if(req->path.back()=='/')
			ListDir();
		else {
			req->path.push_back('/');
			Redirect(req->path,301);
		}
	} else {
		if(CheckModified()) SendFile();
	}
}
void HTTPHandler::HandleFile() {
	int i=req->file_path.find_last_of('/'),j=req->file_path.find_last_of('.');
	if(i>=0&&j>=0&&j>i) {
		string ext=req->file_path.substr(j+1);
		ServerItem *conf=((HTTPServer *)td->server)->conf;
		unordered_map<string,FCGIItem *>::iterator pfcgi=conf->fcgi.find(ext);
		if(pfcgi!=conf->fcgi.end()) {
			FCGIItem *fcgi=pfcgi->second;
			// TODO FCGI
		}
	}
	if(!req->method.compare("GET")||!req->method.compare("HEAD"))
		DoGET();
	else SendError(501);
}
void HTTPHandler::HandleURL(string path) {
	int ret=ParseURL(path);
	if(ret<=0) {
		if(ret) SendError(400,"Invalid URL");
		else Redirect(req->rewrited_path,303);
		res->alive=false;
		return;
	}
	ret=GetRealPath();
	if(ret==-1) SendError(404);
	else HandleFile();
}
bool HTTPHandler::HandleOne() {
	char tmp[256];
	timeval tv;
	req->Init();
	res->Init();
	hconf=NULL;
	int r=ParseRequest();
	res->headers.SetHeader("Server",SERVER);
	tm *ti=gmtime(&timestamp);
	strftime(tmp,256,GMT,ti);
	res->headers.SetHeader("Date",tmp);
	if(r<=0) {
		switch(r) {
		case -2:
			SendError(400,"Line too long");
			break;
		case -3:	// Connection closed
			break;
		default:
			SendError(400);
		}
		return false;
	}
	HandleURL(req->request_uri);
	res->End();
	clock_t delta=clock()-clock_start;
	time_t t=time(NULL);
	strftime(tmp,256,"%Y-%m-%d %H:%M:%S",localtime(&t));
	cout<<tmp<<" \""<<req->request_line<<"\" "<<res->status<<" "<<res->bytes_sent<<" "
		<<delta/CLOCKS_PER_SEC<<"."
		<<setfill('0')<<setw(3)<<delta%CLOCKS_PER_SEC*1000/CLOCKS_PER_SEC<<endl;
	return res->alive;
}

void Handle(LPVOID lpParam) {
	HTTPHandler *h=new HTTPHandler((THREAD_DATA *)lpParam);
	while(h->HandleOne());
	delete h;
}

HTTPServer::HTTPServer(ServerItem *s) {
	conf=s;
	SetAddress(&s->addr);
	SetThreads(s->threads);
	SetHandle(Handle);
}
unsigned __stdcall HTTPServer::_StartServerThread(LPVOID lpParam) {
	HTTP_SERVER_DATA *d=(HTTP_SERVER_DATA *)lpParam;
	d->server->StartServer();
}
void HTTPServer::_StartServer() {
	HTTP_SERVER_DATA *d=new HTTP_SERVER_DATA();
	d->server=this;
	unsigned th;
	HANDLE h=(HANDLE)_beginthreadex(NULL,0,_StartServerThread,d,0,&th);
	CloseHandle(h);
}
